<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <title>bits rain</title>
  <style>

    body {
      display: flex;
      flex-direction: column;
      align-items: center;
      font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
    }

    #scene {
      width: 100%;
      height: 500px;
      display: flex;
      border: 1px solid black;
      overflow: hidden;
      color: #FF0000;
      background-color: #000000;
    }

    #toolbox {
      display: flex;
      flex-direction: column;
      margin-top: 3rem;
      padding: 1rem;
      border-radius: 4px;
      background-color: #F1F1F1;
    }

    .tool {
      display: flex;
      align-items: center;
      margin-bottom: 0.5rem;
    }

    .tool:last-of-type {
      margin-bottom: 0;
    }

    .tool > span {
      margin: 0 1rem 0 0;
      padding: 0;
      flex: 1;
    }

    .tool > input {
      width: 60px;
      border-radius: 3px;
    }

    .tool > input[type="color"]{
      border: none;
      border-color: transparent;
      -webkit-appearance: none;
    }
    
    input::-webkit-color-swatch {
      border: none;
    }

    .column {
      display: flex;
      flex-direction: column;
      align-items: center;
      flex: 1;
    }
  </style>
</head>

<body>
  <div id="scene"></div>
  <div id="toolbox">
    <div class="tool">
      <span>bit color</span>
      <input id="text-color-input" type="color" value="#FF0000" />
    </div>
    <div class="tool">
      <span>background color</span>
      <input id="background-color-input" type="color" value="#000000" />
    </div>
    <div class="tool">
      <span>update interval (ms)</span>
      <input id="speed-input" type="number" min="0" max="1000" value="50" size="1" />
    </div>
    <div class="tool">
      <span>column count</span>
      <input id="column-count-input" type="number" min="0" max="100" value="50" size="4" />
    </div>
    <div class="tool">
      <span>p0</span>
      <input id="p0-input" type="range" min="0" max="1" step="0.05" value="0.3" />
    </div>
    <div class="tool">
      <span>p1</span>
      <input id="p1-input" type="range" min="0" max="1" step="0.05" value="0.3" />
    </div>
  </div>
  <script>
    const isElementOutside = (parent, child) => {
      const parentRect = parent.getBoundingClientRect();
      const childRect = child.getBoundingClientRect();
      return parentRect.top >= childRect.bottom || parentRect.bottom <= childRect.top;
    }

    const getBit = (p0, p1) => {
      const seed = Math.random()
      if (seed <= p0) return '0'
      if (seed <= p1 + p0) return '1'
      return ' '
    }

    const insertColumn = container => {
      const column = document.createElement('div')
      column.classList.add('column')
      container.appendChild(column)
      return column
    }

    const removeColumn = (container, column) => {
      container.removeChild(column)
    }

    const disposeColumns = (container, count) =>
      Array.from(Array(count)).map(() => insertColumn(container))

    const patchColumnCount = (columns, container, nextCount) => {
      const lastCount = columns.length
      if (lastCount === nextCount) return columns
      const countDelta = nextCount - lastCount
      if (countDelta < 0) {
        return columns.filter((column, index) => {
          if (index < lastCount + countDelta) return true
          removeColumn(container, columns[index])
          return false
        })
      }
      const newColumns = disposeColumns(container, countDelta)
      return [
        ...columns,
        ...newColumns
      ]
    }

    const runScene = (scene) => {
      const { container, columns, intervalDuration } = scene
      scene.interval = setInterval(() => {
        columns.forEach((column) => {
          const firstBit = column.firstChild
          const lastBit = column.lastChild
          const bit = document.createElement('div')
          bit.innerText = getBit(scene.p0, scene.p1)
          if (!firstBit) {
            return column.appendChild(bit)
          }
          column.insertBefore(bit, firstBit)
          if (isElementOutside(container, lastBit)) {
            column.removeChild(lastBit)
          }
        })
      }, intervalDuration)
    }
  </script>

  <script>
    const scene = {
      interval: null,
      container: null,
      intervalDuration: 50,
      columns: [],
      columnCount: 50,
      p0: 0.3,
      p1: 0.3
    }

    document.querySelector('#text-color-input').addEventListener('change', event => {
      const { value } = event.target
      scene.container.style.color = value
    })

    document.querySelector('#background-color-input').addEventListener('change', event => {
      const { value } = event.target
      scene.container.style.backgroundColor = value
    })

    document.querySelector('#speed-input').addEventListener('change', event => {
      const { value } = event.target
      clearInterval(scene.interval)
      scene.intervalDuration = value
      runScene(scene)
    })

    document.querySelector('#column-count-input').addEventListener('change', event => {
      const value = Math.abs(event.target.value) > 100 ? 100 : event.target.value
      event.target.value = Math.abs(value)
      clearInterval(scene.interval)
      setTimeout(() => {
        scene.columns = patchColumnCount(scene.columns, scene.container, parseInt(value, 10))
        runScene(scene)
      }, 0)
    })

    const p0Input = document.querySelector('#p0-input')
    const p1Input = document.querySelector('#p1-input')
    const makeUpdateP = (targetName, siblingName, targetInput, siblingInput) => event => {
      const value = Math.abs(event.target.value) > 1 ? 1 : event.target.value
      const pTarget = Math.abs(value)
      targetInput.value = pTarget
      scene[targetName] = pTarget
      const pTargetDelta = 1 - pTarget 
      if (pTarget + scene[siblingName] > 1) {
        scene[siblingName] = pTargetDelta
        siblingInput.value = pTargetDelta
      }
      clearInterval(scene.interval)
      runScene(scene)
    }

    p0Input.addEventListener('change', makeUpdateP('p0', 'p1', p0Input, p1Input))
    p1Input.addEventListener('change', makeUpdateP('p1', 'p0', p1Input, p0Input))

    scene.container = document.querySelector('#scene')
    scene.columns = disposeColumns(scene.container, scene.columnCount)
    runScene(scene)

  </script>
</body>

</html>